package org.lotic.xdb.session;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.XMLWriter;
import org.lotic.ldk.object.ObjectUtil;
import org.lotic.ldk.object.TypeTransition;
import org.lotic.ldk.random.Random;
import org.lotic.ldk.string.StringUtil;
import org.lotic.xdb.manager.XDBConst;
import org.lotic.xdb.pojo.XDBPojo;
import org.lotic.xdb.xml.XMLHandle;

/**
 * XML存储回话
 * 
 * @author lotic
 * 
 */
public class XDBSingleSession {
	/**
	 * 设置自动提交数据.
	 */
	public static boolean AUTO_COMMIT = false;
	/**
	 * 日志对象.
	 */
	private static final Log log = LogFactory.getLog(XDBSingleSession.class);
	/**
	 * 单例Session.
	 */
	private static XDBSingleSession session;
	private XMLHandle handle = new XMLHandle();

	private XDBSingleSession() {

	}

	/**
	 * XML存储文件.
	 */
	private static File file;

	/**
	 * XML存储文件对象.
	 */
	private static Document doc;

	/**
	 * 保存一個pojo進XML存儲.
	 * 
	 * @param pojo
	 */
	public synchronized void save(XDBPojo pojo) {
		Class clazz = pojo.getClass();
		Node objNode = handle.findNode(XDBConst.XDB_XML_OBJECT_PATH, "objName",
				clazz.getName());
		if (objNode == null) {
			objNode = addObjectNode(pojo);
		}

		Map attr = ObjectUtil.pojoAttrToMap(pojo);

		// 新增加一个row 记录.
		String rowId = System.currentTimeMillis() + "_" + Random.getRandom(6);
		attr.put(XDBConst.XDB_XML_OBJECT_ROW_ID, rowId);
		pojo.setRowId(rowId);
		handle.addNode(objNode, XDBConst.XDB_XML_OBJECT_ROW, attr, null);

		if (AUTO_COMMIT) {
			commit();
		}
	}

	/**
	 * 更新对象. 需要对象中rowId村长.
	 * 
	 * @param pojo
	 */
	public synchronized void update(XDBPojo pojo) {

		if (StringUtil.isEmptyOrNull(pojo.getRowId())) {
			if (log.isInfoEnabled()) {
				log.info("rowId为空,无法完成对象的更新!");
			}
		}

		Node rowNode = locationRowNode(pojo.getClass(), pojo.getRowId());
		// 更新行数据.
		handle.updateNode(rowNode, ObjectUtil.pojoAttrToMap(pojo), null);
		if (AUTO_COMMIT) {
			commit();
		}
	}

	/**
	 * 删除数据.
	 * 
	 * @param clazz
	 * @param rowId
	 */
	public synchronized void delete(Class clazz, String rowId) {
		Element row = (Element) locationRowNode(clazz, rowId);
		if (row == null) {
			log.info("没有找到rowid为" + rowId + "节点");
			return;
		}
		row.getParent().remove(row);
		if (AUTO_COMMIT) {
			commit();
		}

	}

	/**
	 * 根据指定条件查询记录.
	 * @param clazz
	 * @param condition
	 * @return
	 */
	public synchronized List<XDBPojo> find(Class clazz, Map condition) {
		return null;
	}

	/**
	 * 根据ROWID查询记录.
	 * @param clazz
	 * @param rowId
	 * @return
	 */
	public synchronized XDBPojo findByRowId(Class clazz, String rowId) {
		Element row = (Element)locationRowNode(clazz, rowId);
		if(row == null) {
			log.info("没有rowId为" + rowId + "的记录");
			return null;
		}
		try {
			Object pj = clazz.newInstance();
			Field[] fields = clazz.getDeclaredFields();
			String fn;
			String s;
			Class type;
			for (Field field : fields) {
				fn = field.getName();
				s = StringUtil.toUpperCaseFirst(fn);
				type = clazz.getMethod("get" + s , null).getReturnType();				
				clazz.getMethod("set" + s, type).
						invoke(pj, TypeTransition.typeTransiton(row.attribute(fn).getValue(), 
								type));;
			}
			clazz.getMethod("setRowId", String.class).invoke(pj,  
					row.attribute("rowId").getValue());
			return (XDBPojo)pj;
		} catch (Exception e) {
			throw new RuntimeException(e);
		} 
	}

	/**
	 * 根据rowID定位到行.
	 * 
	 * @param clazz
	 *            实体类实例.
	 * @param rowId
	 *            rowId.
	 * @return row节点.
	 */
	private Node locationRowNode(Class clazz, String rowId) {
		Node objNode = handle.findNode(XDBConst.XDB_XML_OBJECT_PATH, "objName",
				clazz.getName());
		Node rowNode = handle.findNode(objNode, XDBConst.XDB_XML_OBJECT_ROW,
				XDBConst.XDB_XML_OBJECT_ROW_ID, rowId);
		return rowNode;
	}

	/**
	 * 新增OBJECT 节点。
	 * 
	 * @param pojo
	 */
	private Node addObjectNode(XDBPojo pojo) {
		Class clazz = pojo.getClass();
		Map attr = new HashMap();
		attr.put(XDBConst.XDB_XML_OBJECT_NAME_ATTRIBUTE, clazz.getName());
		attr.put(XDBConst.XDB_XML_OBJECT_ID_ATTRIBUTE,
				System.currentTimeMillis() + "_" + Random.getRandom(5));
		attr.put(XDBConst.XDB_XML_OBJECT_COMMEND, pojo.pojoComment());

		return handle.addNode(XDBConst.XDB_XML_OBJECTS_PATH,
				XDBConst.XDB_XML_OBJECT_NODE, attr, null);
	}

	/**
	 * 刷新,保存数据.
	 */
	public synchronized void reFlesh() {
		FileWriter out;
		try {
			out = new FileWriter(file);
			XMLWriter xw = new XMLWriter(out);
			xw.write(doc);
			xw.close();
		} catch (Exception e) {
			throw new RuntimeException(e);
		}

	}

	/**
	 * 关闭Session.
	 */
	public synchronized void close() {
		reFlesh();
		doc = null;
		file = null;
	}

	/**
	 * 提交数据进文件..
	 */
	public synchronized void commit() {
		reFlesh();
	}

	/*****************************************************/
	/**
	 * 獲取實例
	 * 
	 * @return
	 */
	public static XDBSingleSession getInstance() {
		if (session == null) {
			session = new XDBSingleSession();
		}
		return session;
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		XDBSingleSession.file = file;
	}

	public Document getDoc() {
		return doc;
	}

	public void setDoc(Document doc) {
		XDBSingleSession.doc = doc;
		getHandle().setDocument(XDBSingleSession.doc);
	}

	public XMLHandle getHandle() {
		return handle;
	}

	public void setHandle(XMLHandle handle) {
		this.handle = handle;
	}

}
